package cards;

import java.util.Stack;

/** the solution is a sequence of cards placed on the board according to the card positions
    example without border
*/

public class Solution extends Stack<Candidate>{
	
	
	// The board is an 2D array.
	//   0 1 2 3
	// 0 . . - .
	// 1 - - - .
	// 2 . - - -
	// 3 . . - .
	
	private static final int ROWS = 4;
	private static final int COLUMNS = 4;
	
	private static final int[] rowArray     = { 0, 1, 1, 1, 2, 2, 2, 3 };
	private static final int[] columnArray  = { 2, 0, 1, 2, 1, 2, 3, 2 };
	
	private Candidate[][] board;
	
	// card positions on the board
	// the first card position on the board are
	// {0,2}, {1,0}. {1,1}
	private int[] row;
	private int[] column;
    //  indices of adjacent cards in the solution.
	//                 0   1  2   3   4    5     6    7   
	int [] [] check = {{},{},{1},{0},{2},{3,4},{5,6},{7}}; 
	
	
	public Solution(){
		this.board	=	new Candidate[ROWS][COLUMNS];
		this.row	= 	rowArray;
		this.column = 	columnArray;
		
	}

	
	 // Checks whether a candidate with card CardChar is in 
	 // an adjacent position of the board position (row, column)
	 // @param row, column, candidate
	 // @return Boolean indicating if cardChar is found.
	 // can be used in the methods fits and isCorrect
	private boolean bordersCard(int row, int column, char cardChar){
        /*
        4 cases can be defined.
        North   :   row - 1
        South   :   row + 1
        West    :   col - 1
        East    :   col + 1
        Of course, checking is required to see if these coordinates
        actually exist. So null pointers are not allowed.

        If a case passes, the card with the char provided has been found.
        */
        boolean cardFound = false;
        // North
        if(     row > 0                             &&
                board[row - 1][column] != null      &&
                board[row - 1][column].getCardChar() == cardChar){

            cardFound = true;
        }
        // South
        if(     row + 1 < ROWS                      &&
                board[row + 1][column] != null      &&
                board[row + 1][column].getCardChar() == cardChar){

            cardFound = true;
        }
        // West
        if(     column > 0                          &&
                board[row][column - 1] != null      &&
                board[row][column - 1].getCardChar() == cardChar){

            cardFound = true;
        }
        // East
        if(     column + 1 < COLUMNS                &&
                board[row][column + 1] != null      &&
                board[row][column + 1].getCardChar() == cardChar){

            cardFound = true;
        }

		return cardFound;
    }
	
	
	/**
	 * Checks whether candidate card of same kind.
	 * Checks whether by placing candidate the solution so far still complies with the rules
	 * @param candidate
	 * @return boolean indicating whether this candidate can be put in the
	 * next free position.
	 */
	public boolean fits(Candidate candidate){ 

        int nextLocationIndex = this.size();
        char currentChar = candidate.getCardChar();

        int nextRowCoordinate = row[nextLocationIndex];
        int nextColCoordinate = column[nextLocationIndex];

        return !bordersCard(nextRowCoordinate, nextColCoordinate, currentChar) ;

    }

	public void record(Candidate candidate){
		int i = this.size();                    // i = index in this stack of next for the next candidate
		board[row[i]][column[i]] = candidate;   // x = row, y = column
		this.push(candidate);
	}

	public boolean complete(){
		return this.size() == 8 && isCorrect();
	}

	public void show(){
		System.out.println(this);
	}

	public Candidate eraseRecording(){
	
		int i = this.size()-1;           // i= index of the candidate that is removed from this Stack;
		board[row[i]][column[i]] = null; // remove candidate from board
		return this.pop();
    }
	
	// can be used in method isCorrect
    private char mustBeAdjacentTo(char card){
    	
      if(card=='A'){
    	  return 'K'; 
      }
      if (card=='K'){
    	  return 'Q'; 
      }
      if (card=='Q'){
    	  return 'J';
      }
      return '?'; //error
    }
	
	/**
	 * Checks whether the rules below are fulfilled
	 * For the positions that can be checked for solution so far.
	 * Rules:
	 * Elke aas (ace) grenst (horizontaal of verticaal) aan een heer (king).
	 * Elke heer grenst aan een vrouw (queen).
	 * Elke vrouw grenst aan een boer (jack).
	 * @return true if all checks are correct.
	 */
	// uses methods borderCard and mustBeAdjacent to
	private boolean isCorrect() {
        boolean isCorrect = true;
        Candidate candidate;
        char mustBeAdjacentTo;
        int row;
        int column;
        // Search the board for all the candidates
        for(int index = 0; index < rowArray.length; index++){
            row = rowArray[index];
            column = columnArray[index];
            candidate = board[row][column];
            // If we found a candidate
            if(candidate != null){
                // Get the char where the candidate must be adjacent to.
                mustBeAdjacentTo = mustBeAdjacentTo(candidate.getCardChar());
                // If that char is not a ? and the candidate is not adjacent to that char
                if( mustBeAdjacentTo != '?' && !bordersCard(
                        row,
                        column,
                        mustBeAdjacentTo )
                        ) {
                    // Solution is incorrect.
                    isCorrect = false;
                }
            }
        }

        // Return correctness of the solution.
        return isCorrect;
    }     
            
	
	/**
	 * @return a representation of the solution on the board
	 */
     public String toString(){
         // The board is an 2D array.
         //   0 1 2 3
         // 0 . . - .
         // 1 - - - .
         // 2 . - - -
         // 3 . . - .

         String indentation = " ";
         StringBuilder buffer = new StringBuilder();
         // First create the column indices on the top
         buffer.append(indentation).append(indentation);
         for(int i = 0; i < ROWS; i++ ){
             buffer.append(indentation).append(i);
         }

         buffer.append("\n");

         for(int j = 0; j < ROWS; j++){
             // Create the row index
             buffer.append(indentation).append(j);
             // Go through the row for each column
             for (int x = 0; x < COLUMNS; x++){
                 buffer.append(indentation);
                 if(board[x][j] != null){
                     buffer.append(board[x][j].getCardChar());
                 }else{
                     buffer.append('-');
                 }
             }
             buffer.append("\n");
         }

	    return buffer.toString();
	}    

}
